using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq.Dynamic.Core;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using SanJ.FreeCare.Permissions;
using SanJ.FreeCare.Medications;
using MiniExcelLibs;
using Volo.Abp.Content;
using Volo.Abp.Authorization;
using Volo.Abp.Caching;
using Microsoft.Extensions.Caching.Distributed;
using SanJ.FreeCare.Shared;

namespace SanJ.FreeCare.Medications
{

    public abstract class MedicationsAppServiceBase : FreeCareAppService
    {
        protected IDistributedCache<MedicationExcelDownloadTokenCacheItem, string> _excelDownloadTokenCache;
        protected IMedicationRepository _medicationRepository;
        protected MedicationManager _medicationManager;

        public MedicationsAppServiceBase(IMedicationRepository medicationRepository, MedicationManager medicationManager, IDistributedCache<MedicationExcelDownloadTokenCacheItem, string> excelDownloadTokenCache)
        {
            _excelDownloadTokenCache = excelDownloadTokenCache;
            _medicationRepository = medicationRepository;
            _medicationManager = medicationManager;
        }

        public virtual async Task<PagedResultDto<MedicationDto>> GetListAsync(GetMedicationsInput input)
        {
            var totalCount = await _medicationRepository.GetCountAsync(input.FilterText, input.Code, input.StatusCode, input.KeyWord, input.ProductName, input.ProductNamePY, input.ProductNameWB, input.ProductNameEN, input.Alias, input.AliasPY, input.AliasWB, input.AliasEN, input.ScientificName, input.ScientificNamePY, input.ScientificNameWB, input.ScientificNameEN, input.AntiLevelCode, input.AntiClassCode1, input.AntiClassCode2, input.AnitDDDUnitCode, input.AnitDDDValueMin, input.AnitDDDValueMax, input.AntiOnceMinValueMin, input.AntiOnceMinValueMax, input.AntiOnceMaxValueMin, input.AntiOnceMaxValueMax, input.AntiOnceUnitCode, input.AntiFrequencyMinMin, input.AntiFrequencyMinMax, input.AntiFrequencyMaxMin, input.AntiFrequencyMaxMax, input.GBCode, input.TypeCode, input.ClassCode, input.FeeTypeCode, input.SpecialTypeCode, input.LevelCode, input.DoseFormCode, input.Specification, input.BaseUnitCode, input.ClinicSaleUnitCode, input.ClinicSaleVolumeMin, input.ClinicSaleVolumeMax, input.InpatientSaleUnitCode, input.InpatientSaleVolume, input.TransportUnitCode, input.TransportVolume, input.DefaultDoseUnitCode, input.DefultDoseVolumeMin, input.DefultDoseVolumeMax, input.SecondDoseUnitCode, input.SecondDoseVolumeMin, input.SecondDoseVolumeMax, input.RetailPriceMin, input.RetailPriceMax, input.DefaultUsageCode, input.DefaultFrequencyCode, input.DefaultDoseVolumeMin, input.DefaultDoseVolumeMax, input.SkinTestFlag, input.SkinTestNeedFlag, input.SkinTestTypeFlag, input.SkinTestCode);
            var items = await _medicationRepository.GetListAsync(input.FilterText, input.Code, input.StatusCode, input.KeyWord, input.ProductName, input.ProductNamePY, input.ProductNameWB, input.ProductNameEN, input.Alias, input.AliasPY, input.AliasWB, input.AliasEN, input.ScientificName, input.ScientificNamePY, input.ScientificNameWB, input.ScientificNameEN, input.AntiLevelCode, input.AntiClassCode1, input.AntiClassCode2, input.AnitDDDUnitCode, input.AnitDDDValueMin, input.AnitDDDValueMax, input.AntiOnceMinValueMin, input.AntiOnceMinValueMax, input.AntiOnceMaxValueMin, input.AntiOnceMaxValueMax, input.AntiOnceUnitCode, input.AntiFrequencyMinMin, input.AntiFrequencyMinMax, input.AntiFrequencyMaxMin, input.AntiFrequencyMaxMax, input.GBCode, input.TypeCode, input.ClassCode, input.FeeTypeCode, input.SpecialTypeCode, input.LevelCode, input.DoseFormCode, input.Specification, input.BaseUnitCode, input.ClinicSaleUnitCode, input.ClinicSaleVolumeMin, input.ClinicSaleVolumeMax, input.InpatientSaleUnitCode, input.InpatientSaleVolume, input.TransportUnitCode, input.TransportVolume, input.DefaultDoseUnitCode, input.DefultDoseVolumeMin, input.DefultDoseVolumeMax, input.SecondDoseUnitCode, input.SecondDoseVolumeMin, input.SecondDoseVolumeMax, input.RetailPriceMin, input.RetailPriceMax, input.DefaultUsageCode, input.DefaultFrequencyCode, input.DefaultDoseVolumeMin, input.DefaultDoseVolumeMax, input.SkinTestFlag, input.SkinTestNeedFlag, input.SkinTestTypeFlag, input.SkinTestCode, input.Sorting, input.MaxResultCount, input.SkipCount);

            return new PagedResultDto<MedicationDto>
            {
                TotalCount = totalCount,
                Items = ObjectMapper.Map<List<Medication>, List<MedicationDto>>(items)
            };
        }

        public virtual async Task<MedicationDto> GetAsync(Guid id)
        {
            return ObjectMapper.Map<Medication, MedicationDto>(await _medicationRepository.GetAsync(id));
        }

        [Authorize(FreeCarePermissions.Medications.Delete)]
        public virtual async Task DeleteAsync(Guid id)
        {
            await _medicationRepository.DeleteAsync(id);
        }

        [Authorize(FreeCarePermissions.Medications.Create)]
        public virtual async Task<MedicationDto> CreateAsync(MedicationCreateDto input)
        {

            var medication = await _medicationManager.CreateAsync(
            input.AnitDDDValue, input.ClinicSaleVolume, input.DefultDoseVolume, input.SecondDoseVolume, input.RetailPrice, input.DefaultDoseVolume, input.Code, input.StatusCode, input.KeyWord, input.ProductName, input.ProductNamePY, input.ProductNameWB, input.ProductNameEN, input.Alias, input.AliasPY, input.AliasWB, input.AliasEN, input.ScientificName, input.ScientificNamePY, input.ScientificNameWB, input.ScientificNameEN, input.AntiLevelCode, input.AntiClassCode1, input.AntiClassCode2, input.AnitDDDUnitCode, input.AntiOnceMinValue, input.AntiOnceMaxValue, input.AntiOnceUnitCode, input.AntiFrequencyMin, input.AntiFrequencyMax, input.GBCode, input.TypeCode, input.ClassCode, input.FeeTypeCode, input.SpecialTypeCode, input.LevelCode, input.DoseFormCode, input.Specification, input.BaseUnitCode, input.ClinicSaleUnitCode, input.InpatientSaleUnitCode, input.InpatientSaleVolume, input.TransportUnitCode, input.TransportVolume, input.DefaultDoseUnitCode, input.SecondDoseUnitCode, input.DefaultUsageCode, input.DefaultFrequencyCode, input.SkinTestFlag, input.SkinTestNeedFlag, input.SkinTestTypeFlag, input.SkinTestCode
            );

            return ObjectMapper.Map<Medication, MedicationDto>(medication);
        }

        [Authorize(FreeCarePermissions.Medications.Edit)]
        public virtual async Task<MedicationDto> UpdateAsync(Guid id, MedicationUpdateDto input)
        {
            var medication = await _medicationManager.UpdateAsync(
            id,
            input.AnitDDDValue, input.ClinicSaleVolume, input.DefultDoseVolume, input.SecondDoseVolume, input.RetailPrice, input.DefaultDoseVolume, input.Code, input.StatusCode, input.KeyWord, input.ProductName, input.ProductNamePY, input.ProductNameWB, input.ProductNameEN, input.Alias, input.AliasPY, input.AliasWB, input.AliasEN, input.ScientificName, input.ScientificNamePY, input.ScientificNameWB, input.ScientificNameEN, input.AntiLevelCode, input.AntiClassCode1, input.AntiClassCode2, input.AnitDDDUnitCode, input.AntiOnceMinValue, input.AntiOnceMaxValue, input.AntiOnceUnitCode, input.AntiFrequencyMin, input.AntiFrequencyMax, input.GBCode, input.TypeCode, input.ClassCode, input.FeeTypeCode, input.SpecialTypeCode, input.LevelCode, input.DoseFormCode, input.Specification, input.BaseUnitCode, input.ClinicSaleUnitCode, input.InpatientSaleUnitCode, input.InpatientSaleVolume, input.TransportUnitCode, input.TransportVolume, input.DefaultDoseUnitCode, input.SecondDoseUnitCode, input.DefaultUsageCode, input.DefaultFrequencyCode, input.SkinTestFlag, input.SkinTestNeedFlag, input.SkinTestTypeFlag, input.SkinTestCode, input.ConcurrencyStamp
            );

            return ObjectMapper.Map<Medication, MedicationDto>(medication);
        }

        [AllowAnonymous]
        public virtual async Task<IRemoteStreamContent> GetListAsExcelFileAsync(MedicationExcelDownloadDto input)
        {
            var downloadToken = await _excelDownloadTokenCache.GetAsync(input.DownloadToken);
            if (downloadToken == null || input.DownloadToken != downloadToken.Token)
            {
                throw new AbpAuthorizationException("Invalid download token: " + input.DownloadToken);
            }

            var items = await _medicationRepository.GetListAsync(input.FilterText, input.Code, input.StatusCode, input.KeyWord, input.ProductName, input.ProductNamePY, input.ProductNameWB, input.ProductNameEN, input.Alias, input.AliasPY, input.AliasWB, input.AliasEN, input.ScientificName, input.ScientificNamePY, input.ScientificNameWB, input.ScientificNameEN, input.AntiLevelCode, input.AntiClassCode1, input.AntiClassCode2, input.AnitDDDUnitCode, input.AnitDDDValueMin, input.AnitDDDValueMax, input.AntiOnceMinValueMin, input.AntiOnceMinValueMax, input.AntiOnceMaxValueMin, input.AntiOnceMaxValueMax, input.AntiOnceUnitCode, input.AntiFrequencyMinMin, input.AntiFrequencyMinMax, input.AntiFrequencyMaxMin, input.AntiFrequencyMaxMax, input.GBCode, input.TypeCode, input.ClassCode, input.FeeTypeCode, input.SpecialTypeCode, input.LevelCode, input.DoseFormCode, input.Specification, input.BaseUnitCode, input.ClinicSaleUnitCode, input.ClinicSaleVolumeMin, input.ClinicSaleVolumeMax, input.InpatientSaleUnitCode, input.InpatientSaleVolume, input.TransportUnitCode, input.TransportVolume, input.DefaultDoseUnitCode, input.DefultDoseVolumeMin, input.DefultDoseVolumeMax, input.SecondDoseUnitCode, input.SecondDoseVolumeMin, input.SecondDoseVolumeMax, input.RetailPriceMin, input.RetailPriceMax, input.DefaultUsageCode, input.DefaultFrequencyCode, input.DefaultDoseVolumeMin, input.DefaultDoseVolumeMax, input.SkinTestFlag, input.SkinTestNeedFlag, input.SkinTestTypeFlag, input.SkinTestCode);

            var memoryStream = new MemoryStream();
            await memoryStream.SaveAsAsync(ObjectMapper.Map<List<Medication>, List<MedicationExcelDto>>(items));
            memoryStream.Seek(0, SeekOrigin.Begin);

            return new RemoteStreamContent(memoryStream, "Medications.xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        }

        public virtual async Task<SanJ.FreeCare.Shared.DownloadTokenResultDto> GetDownloadTokenAsync()
        {
            var token = Guid.NewGuid().ToString("N");

            await _excelDownloadTokenCache.SetAsync(
                token,
                new MedicationExcelDownloadTokenCacheItem { Token = token },
                new DistributedCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30)
                });

            return new SanJ.FreeCare.Shared.DownloadTokenResultDto
            {
                Token = token
            };
        }
        [Authorize(FreeCarePermissions.Medications.Delete)]
        public virtual async Task DeleteByIdsAsync(List<Guid> medicationIds)
        {
            await _medicationRepository.DeleteManyAsync(medicationIds);
        }

        [Authorize(FreeCarePermissions.Medications.Delete)]
        public virtual async Task DeleteAllAsync(GetMedicationsInput input)
        {
            await _medicationRepository.DeleteAllAsync(input.FilterText, input.Code, input.StatusCode, input.KeyWord, input.ProductName, input.ProductNamePY, input.ProductNameWB, input.ProductNameEN, input.Alias, input.AliasPY, input.AliasWB, input.AliasEN, input.ScientificName, input.ScientificNamePY, input.ScientificNameWB, input.ScientificNameEN, input.AntiLevelCode, input.AntiClassCode1, input.AntiClassCode2, input.AnitDDDUnitCode, input.AnitDDDValueMin, input.AnitDDDValueMax, input.AntiOnceMinValueMin, input.AntiOnceMinValueMax, input.AntiOnceMaxValueMin, input.AntiOnceMaxValueMax, input.AntiOnceUnitCode, input.AntiFrequencyMinMin, input.AntiFrequencyMinMax, input.AntiFrequencyMaxMin, input.AntiFrequencyMaxMax, input.GBCode, input.TypeCode, input.ClassCode, input.FeeTypeCode, input.SpecialTypeCode, input.LevelCode, input.DoseFormCode, input.Specification, input.BaseUnitCode, input.ClinicSaleUnitCode, input.ClinicSaleVolumeMin, input.ClinicSaleVolumeMax, input.InpatientSaleUnitCode, input.InpatientSaleVolume, input.TransportUnitCode, input.TransportVolume, input.DefaultDoseUnitCode, input.DefultDoseVolumeMin, input.DefultDoseVolumeMax, input.SecondDoseUnitCode, input.SecondDoseVolumeMin, input.SecondDoseVolumeMax, input.RetailPriceMin, input.RetailPriceMax, input.DefaultUsageCode, input.DefaultFrequencyCode, input.DefaultDoseVolumeMin, input.DefaultDoseVolumeMax, input.SkinTestFlag, input.SkinTestNeedFlag, input.SkinTestTypeFlag, input.SkinTestCode);
        }
    }
}